/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once

#include "Hogshead.Common.h"
#include <Windows.h>

/**
define NEW_DELETE_FACTORY for standard settings
	news and deletes and does not keep track of anything, just an alias for new and delete basically
define NEW_DELETE_RECORD_FACTORY for standard settings plus record keeping
	news and deletes things, but also keeps track of how many of each thing we need
	writes the results to an xml file
define PREALLOCATE_FACTORY for preallocation of things read in from XML
	allocates based on an xml file
**/


#define NEW_DELETE_FACTORY
//#define NEW_DELETE_RECORD_FACTORY
//#define PREALLOCATED_FACTORY

namespace Hogshead
{
	namespace Core
	{
		class IHObject;

#ifdef NEW_DELETE_FACTORY
		class Factory
		{

		public:
			static void initialize();
			template <typename T> static T* acquire()
			{
				return new T();
			};
			static void* acquireMemoryBlock(UINT in_byte_size)
			{
				void* result = malloc(in_byte_size);
				if(!result)
					error("Couldn't create a block of memory as large as requested.");

				return result;
			}
			static IHObject* acquire(IHObject* in_object);
			static void release(IHObject* in_object);
			static void releaseMemoryBlock(void* in_memory_block)
			{
				if(in_memory_block)
					free(in_memory_block);
				else
					error("You tried to release the NULL pointer as a memory block.");
			}
			static void safeRelease(IHObject** in_object);
			template <typename T> static int registerHObject()
			{
				if(_register_buffer == NULL)
					_register_buffer = new Vector<IHObject*>();
				_register_buffer->add((IHObject*)new T());
				return _register_buffer->size() - 1;

			};
			static void finalize();
		private:
			static Logger* _logger;
			static Vector<IHObject*>* _register_buffer;
		};
#endif

#ifdef NEW_DELETE_RECORD_FACTORY
		class Factory
		{
		public:
			static void initialize();
			template <typename T> static T* acquire()
			{
				// get our id
				int t_id = T::pre_allocated_id;
				// update stats for this preallocated type
				m_total_preallocated_ever_used[t_id]++;
				m_preallocated_in_use[t_id]++;
				if(m_preallocated_in_use[t_id] > m_max_preallocated_in_use[t_id])
					m_max_preallocated_in_use[t_id] = m_preallocated_in_use[t_id];

				// return a new T
				return new T();
			};
			static IPreallocated* acquire(IPreallocated* in_object);
			static void release(IPreallocated* in_object);
			static void safeRelease(IPreallocated** in_object);
			template <typename T> static int registerPreallocated()
			{
				// this might not be initialized yet
				if(m_prototypes == NULL)
					m_prototypes = new DataStructures::DynamicList<CoreEngine::IPreallocated*>();
				
				T::pre_allocated_id = m_classes++;
				T* prototype = new T();
				// save a  
				m_prototypes->addLast((IPreallocated*)prototype);

				Hogshead::DataStructures::HStringStream hss;
				hss << "Registered PreAllocated #" << prototype->myPreAllocatedID() << ": " <<  prototype->preAllocatedName();

				// this might be not initialized yet
				if(m_logger == NULL)
					m_logger = new Hogshead::Utils::Logger("logs\\factory.log");

				m_logger->writeLog(Hogshead::Utils::Logger::Manager, hss.toString());

				return T::pre_allocated_id;
			};
			static void finalize();
		private:
			// total number of classes registered to the factroy
			static unsigned int m_classes;
			// total number of each class in use at the moment
			static unsigned int* m_preallocated_in_use;
			// max number of each class ever used at one time
			static unsigned int* m_max_preallocated_in_use;
			// total number of acquires of each class made
			static unsigned int* m_total_preallocated_ever_used;
			// prototypes of each class
			static DataStructures::DynamicList<CoreEngine::IPreallocated*>* m_prototypes;

			// logger for the factory
			static Hogshead::Utils::Logger* m_logger;
		};
#endif

#ifdef PREALLOCATE_FACTORY
		class Factory
		{
		public:
			static void initialize();
			template <typename T> static T* acquire()
			{
				int t_id = T::pre_allocated_id;
				
				T* result = (T*)m_object_pools[t_id]->pop();
				if(result == NULL)
				{
					result = new T();
				}
				
				return result;
			};
			static IPreallocated* acquire(IPreallocated* in_object);
			static void release(IPreallocated* in_object);
			static void safeRelease(IPreallocated** in_object);

			// happens before initialize does
			template <typename T> static int registerPreallocated()
			{
				// this might not be initialized yet
				if(m_prototypes == NULL)
					m_prototypes = new DataStructures::DynamicList<CoreEngine::IPreallocated*>();
				
				T::pre_allocated_id = m_classes++;
				T* prototype = new T();
				// save a  
				m_prototypes->addLast((IPreallocated*)prototype);

				Hogshead::DataStructures::HStringStream hss;
				hss << "Registered PreAllocated #" << prototype->myPreAllocatedID() << ": " <<  prototype->preAllocatedName();

				// this might be not initialized yet
				if(m_logger == NULL)
					m_logger = new Hogshead::Utils::Logger("logs\\factory.log");

				m_logger->writeLog(Hogshead::Utils::Logger::Manager, hss.toString());

				return T::pre_allocated_id;
			};
			static void finalize();
		private:
			// total number of classes registered to the factroy
			static unsigned int m_classes;

			// prototypes of each class
			static DataStructures::DynamicList<CoreEngine::IPreallocated*>* m_prototypes;

			// logger for the factory
			static Hogshead::Utils::Logger* m_logger;
			
			// storage of preallocated
			class ObjectPool;
			static ObjectPool** m_object_pools;

			class ObjectPool
			{
			public:
				ObjectPool(void);
				~ObjectPool(void);
				// populate the pool with prealloateds				
				void initialize(IPreallocated* in_prototype, unsigned int in_count);
				void push(IPreallocated* in_object);

				IPreallocated* pop();
				
				int size();

				// an array list basically
			private:
				Hogshead::DataStructures::DynamicList<IPreallocated**> data;
				int pool_size;
				int capacity;

				static const int ROW_WIDTH = 1024;
			};
		};
#endif
	}
}